/*************************************************************************
 *                                                                       *
 * This file is part of the DogosFrame distribution.  For full copyright *
 * information, see COPYRIGHT and COPYING.LESSER.                        *
 *                                                                       *
 * Author:           sqbang                                              *
 * Email:            sqbang@ustc.edu                                     *
 *                                                                       *
 *************************************************************************/

//declaration header
#include "COMMON/pars/InputDatabase.h"

//COMMON headers
#include "COMMON/tbox/MathUtilities.h"
#include "COMMON/tbox/LogMessage.h"

namespace DOGOS
{

InputDatabase::InputDatabase (
    Database* db):
    d_database  (db),
    d_is_top_db (true)
{
    TBOX_ASSERT (db);
    d_nested_name = db->getName();
}



InputDatabase::~InputDatabase()
{
    d_key_line_number.clear();
    Utilities::clear_pointer_object (d_sub_databases);
}



void
InputDatabase::set_key_line_number (
    const std::string& key,
    const int line_number)
{
    //TBOX_ASSERT_MSG (this->key_exists (key), "key = \"" << key << "\"");
    if (d_key_line_number.count (key) > 0)
    {
        this->show_unknown_error (
            key,
            "the key is redefined");
    }
    d_key_line_number.insert (std::make_pair (key, line_number));
}



std::string
InputDatabase::get_line_number_message (
    const std::string& key)const
{
    int line_number = 0;
    if (this->key_exists (key))
    {
        line_number = this->get_line_number (key);
    }
    else
    {
        line_number = this->get_line_number();
    }

    std::ostringstream oss;
    oss << "in \"" << this->get_file_name() << "\"\nin line " << line_number << '\n';
    oss << "in \"" << this->get_nested_name () << "\"";
    return oss.str();
}



std::string
InputDatabase::get_line_number_message () const
{
    const int line_number = this->get_line_number ();
    std::ostringstream oss;
    oss << "in \"" << this->get_file_name() << "\"\nin line " << line_number << '\n';
    oss << "in \"" << this->get_nested_name () << "\"";
    return oss.str();
}



bool
InputDatabase::key_exists (
    const std::string& key) const
{
    TBOX_ASSERT (d_database);
    return (d_database->keyExists (key));
}



std::vector<std::string>
InputDatabase::get_keys() const
{
    TBOX_ASSERT (d_database);
    return Utilities::trans_set_to_vec (Utilities::trans_vec_to_set (d_database->getAllKeys()) );
}



int
InputDatabase::get_array_size (
    const std::string& key) const
{
    TBOX_ASSERT (d_database);
    return d_database->getArraySize (key);
}



bool
InputDatabase::use_default_value (
    const std::string& key) const
{
    if (this->key_exists (key))
    {
        if (this->is_string (key))
        {
            const std::vector<std::string>& value = this->get_string_vector (key);
            if (value.size() == 1 && value[0] == "DOGOS_DEFAULT_VALUE")
            {
                return true;
            }
        }
    }

    return false;
}



DataType
InputDatabase::get_key_type (
    const std::string& key) const
{
    if (!this->key_exists (key))
    {
        return DataType::INVALID_VALUE;
    }
    else
    {
        if (this->is_bool (key))
        {
            return DataType::BOOL;
        }
        else if (this->is_char (key))
        {
            return DataType::CHAR;
        }
        else if (this->is_integer (key))
        {
            return DataType::INTEGER;
        }
        else if (this->is_float (key))
        {
            return DataType::FLOAT;
        }
        else if (this->is_double (key))
        {
            return DataType::DOUBLE;
        }
        else if (this->is_complex (key))
        {
            return DataType::COMPLEX;
        }
        else if (this->is_string (key))
        {
            return DataType::STRING;
        }
        else if (this->is_database (key))
        {
            return DataType::DATABASE;
        }
        else
        {
            return DataType::UNKNOWN;
        }
    }
}



void
InputDatabase::insert_allowable_key (
    const std::string& allowable_key) const
{
    d_allowable_keys.insert (allowable_key);
}



void
InputDatabase::insert_allowable_keys (
    const std::set<std::string>& allowable_keys) const
{
    Utilities::insert (allowable_keys, d_allowable_keys);
}



void
InputDatabase::insert_allowable_key_with_items (
    const std::string& allowable_key) const
{
    d_allowable_keys.insert (allowable_key);

    this->check_optional_key (allowable_key, DataType::STRING, DefaultSwitch::OFF, ArraySize::ANY);
    this->check_duplicated_string_value (allowable_key);
    if (this->key_exists (allowable_key))
    {
        Utilities::insert (this->get_string_vector (allowable_key), d_allowable_keys);
    }
}



void
InputDatabase::check_incompatibility_keys (
    const std::set<std::string>& key_group1,
    const std::set<std::string>& key_group2) const
{
    for (std::set<std::string>::const_iterator p1 = key_group1.begin();
            p1 != key_group1.end(); ++p1)
    {
        for (std::set<std::string>::const_iterator p2 = key_group2.begin();
                p2 != key_group2.end(); ++p2)
        {
            this->check_incompatibility_keys (*p1, *p2);
        }
    }
}



void
InputDatabase::check_incompatibility_keys (
    const std::string& key_1,
    const std::string& key_2) const
{
    if (this->key_exists (key_1) && this->key_exists (key_2))
    {
        this->show_error_of_disallowable_mutually_exclusive (
            key_1,
            key_2);
    }
}



void
InputDatabase::check_either_or_keys (
    const std::string& key_1,
    const std::string& key_2) const
{
    this->check_incompatibility_keys (key_1, key_2);

    if (!this->key_exists (key_1) && !this->key_exists (key_2))
    {
        this->show_unknown_error (
            "\"" + key_1 + "\" and \"" + key_2 + "\" can not miss neither");
    }
}



void
InputDatabase::check_allowable_keys() const
{
    const std::vector<std::string>& keys = this->get_keys();

    /*
     * first check all thos type is string array
     * for example, if a command has type:
     *     key = item1, item2
     *     item1 = ...
     *     item2 = ...
     * then check key first to avoid some error message
     * that is hard to understand.
     */
    for (std::size_t i = 0; i < keys.size(); ++i)
    {
        const std::string& key = keys[i];
        if (this->is_string (key) && this->get_array_size (key) > 1)
        {
            this->check_allowable_key (key);
        }
    }

    /*
     * then check string
     */
    for (std::size_t i = 0; i < keys.size(); ++i)
    {
        const std::string& key = keys[i];
        if (this->is_string (key))
        {
            this->check_allowable_key (key);
        }
    }

    /*
     * then check left
     */
    for (std::size_t i = 0; i < keys.size(); ++i)
    {
        this->check_allowable_key (keys[i]);
    }
}



void
InputDatabase::check_allowable_key (
    const std::string& key) const
{
    if (d_allowable_keys.count (key) == 0)
    {
        /*
         * try to find the most similar key to give usr a tip
         */
        unsigned int similar_key_len = 0;
        std::string similar_key;
        for (std::set<std::string>::const_iterator pos = d_allowable_keys.begin();
                pos != d_allowable_keys.end(); ++pos)
        {
            const unsigned int similar_to_current_key = Utilities::get_num_of_same_chars (key, *pos);
            if (similar_to_current_key > similar_key_len)
            {
                similar_key_len = similar_to_current_key;
                similar_key = *pos;
            }
        }
        if (similar_key_len > 1)
        {
            this->show_error_of_unrecognized_key (
                key,
                similar_key,
                d_allowable_keys);
        }
        else
        {
            this->show_error_of_unrecognized_key (
                key,
                d_allowable_keys);
        }
    }
}



void
InputDatabase::check_disallowable_key (
    const std::string& disallowable_key,
    const std::string& reason) const
{
    if (this->key_exists (disallowable_key))
    {
        std::ostringstream oss;
        oss << "key of \"" << disallowable_key << "\" is disallowable, the reason is:\n";
        oss << reason;
        this->show_unknown_error (
            oss.str());
    }
}



void
InputDatabase::check_disallowable_keys (
    const std::set<std::string>& disallowable_keys,
    const std::string& reason) const
{
    for (std::set<std::string>::const_iterator pos = disallowable_keys.begin();
            pos != disallowable_keys.end(); ++pos)
    {
        const std::string& disallowable_key = *pos;
        if (this->key_exists (disallowable_key))
        {
            std::ostringstream oss;
            oss << "key of \"" << disallowable_key << "\" is disallowable, the reason is:\n";
            oss << reason;
            this->show_unknown_error (
                oss.str());
        }
    }
}



void
InputDatabase::check_optional_key (
    const std::string& key,
    const DataType data_type,
    const DefaultSwitch default_type,
    const ArraySize array_size) const
{
    const bool can_use_default_value = (default_type == DefaultSwitch::ON);
    const bool is_array = (array_size == ArraySize::ANY);
    if (!this->key_exists (key))
    {
        return;
    }

    /*
     * if key=="DOGOS_DEFAULT_VALUE", do some special check.
     * because may be the key is a int, double... type while
     * DOGOS_DEFAULT_VALUE is supported
     */
    if (this->is_string (key))
    {
        const std::vector<std::string>& values = this->get_string_vector (key);
        if ( (values.size() == 1) && values[0] == "DOGOS_DEFAULT_VALUE")
        {
            if (can_use_default_value)
            {
                return;
            }
            else
            {
                this->show_error_of_using_default_value (key);
            }
        }
    }

    if (this->get_array_size (key) > 1 && !is_array)
    {
        this->show_error_of_using_array (key);
    }

    /*
     * if is not DOGOS_DEFAULT_VALUE, check directly.
     * now compare data_type with Utilities::get_key_type()
     * because if data_type is DOUBLE, then int and float will
     * take as logical.
     */
    switch (data_type)
    {
    case DataType::DATABASE:
        {
            if (!this->is_database (key))
            {
                this->show_error_of_disallowable_type (
                    key,
                    Utilities::enum_to_string (data_type));
            }
            return;
        }
    case DataType::BOOL:
        {
            if (!this->is_bool (key))
            {
                this->show_error_of_disallowable_type (
                    key,
                    Utilities::enum_to_string (data_type));
            }
            return;
        }
    case DataType::CHAR:
        {
            if (!this->is_char (key))
            {
                this->show_error_of_disallowable_type (
                    key,
                    Utilities::enum_to_string (data_type));
            }
            return;
        }
    case DataType::COMPLEX:
        {
            if (!this->is_complex (key))
            {
                this->show_error_of_disallowable_type (
                    key,
                    Utilities::enum_to_string (data_type));
            }
            return;
        }
    case DataType::DOUBLE:
        {
            if (!this->is_double (key))
            {
                this->show_error_of_disallowable_type (
                    key,
                    Utilities::enum_to_string (data_type));
            }
            return;
        }
    case DataType::FLOAT:
        {
            if (!this->is_float (key))
            {
                this->show_error_of_disallowable_type (
                    key,
                    Utilities::enum_to_string (data_type));
            }
            return;
        }
    case DataType::INTEGER:
        {
            if (!this->is_integer (key))
            {
                this->show_error_of_disallowable_type (
                    key,
                    Utilities::enum_to_string (data_type));
            }
            return;
        }
    case DataType::STRING:
        {
            if (!this->is_string (key))
            {
                this->show_error_of_disallowable_type (
                    key,
                    Utilities::enum_to_string (data_type));
            }
            return;
        }
    case DataType::UNKNOWN:
        {
            return;
        }
    default:
        {
            TBOX_ERROR ("unrecognized type");
        }
    }

    if (data_type != this->get_key_type (key))
    {
        this->show_error_of_disallowable_type (
            key,
            Utilities::enum_to_string (data_type));
    }
}



void
InputDatabase::check_requisite_key (
    const std::string& key,
    const DataType data_type,
    const DefaultSwitch can_use_default_value,
    const ArraySize is_array) const
{
    if (!this->key_exists (key))
    {
        this->show_error_of_missing_key (
            key);
    }
    else
    {
        this->check_optional_key (key, data_type, can_use_default_value, is_array);
    }
}



void
InputDatabase::check_requisite_database (
    const std::string& key) const
{
    if (this->key_exists (key))
    {
        this->check_requisite_key (key, DataType::DATABASE, DefaultSwitch::OFF, ArraySize::ONE);
    }
    else
    {
        this->put_database (key);
    }
}



void
InputDatabase::check_blank_string_key (
    const std::string& key) const
{
    if (this->key_exists (key))
    {
        return;
    }

    this->check_requisite_key (key, DataType::STRING, DefaultSwitch::OFF, ArraySize::ANY);
    const std::vector<std::string>& array = this->get_string_vector (key);

    for (std::size_t i = 0; i < array.size(); ++i)
    {
        if (Utilities::is_blank_string (array[i]))
        {
            this->show_error_of_using_blank_string (key);
        }
    }
}



void
InputDatabase::check_duplicated_string_value (
    const std::string& key) const
{
    if (!this->key_exists (key))
    {
        return;
    }

    this->check_requisite_key (key, DataType::STRING, DefaultSwitch::OFF, ArraySize::ANY);
    const std::vector<std::string>& array = this->get_string_vector (key);

    std::map<std::string, int> data;
    std::ostringstream os;
    for (std::size_t i = 0; i < array.size(); ++i)
    {
        if (data.count (array[i]) > 0)
        {
            const int pre_num = data.find (array[i])->second;
            os << "keyword \"" << key << "\" can't have duplicated value of:\n";
            os << key << "[" << pre_num << "] = ";
            os << key << "[" << i << "] = " << array[i] << std::endl;
            this->show_unknown_error (
                key,
                os.str());
        }

        data.insert (std::make_pair (array[i], i));
    }

    this->check_blank_string_key (key);
}



void
InputDatabase::check_duplicated_integer_value (
    const std::string& key) const
{
    if (!this->key_exists (key))
    {
        return;
    }

    this->check_requisite_key (key, DataType::INTEGER, DefaultSwitch::OFF, ArraySize::ANY);
    const std::vector<int>& array = this->get_integer_vector (key);

    std::map<int, int> data;
    std::ostringstream os;
    for (std::size_t i = 0; i < array.size(); ++i)
    {
        if (data.count (array[i]) > 0)
        {
            const int pre_num = data.find (array[i])->second;
            os << "keyword \"" << key << "\" can't have duplicated value of:\n";
            os << key << "[" << pre_num << "] = ";
            os << key << "[" << i << "] = " << array[i] << std::endl;
            this->show_unknown_error (
                key,
                os.str());
        }

        data.insert (std::make_pair (array[i], i));
    }
}



void
InputDatabase::check_allowable_string_value (
    const std::string& key,
    const std::string& tip_info,
    const std::set<std::string>& allowable_values) const
{
    if (!this->key_exists (key))
    {
        return;
    }

    TBOX_ASSERT (this->is_string (key));
    const std::vector<std::string>& value_array = this->get_string_vector (key);
    for (std::size_t i = 0; i < value_array.size(); ++i)
    {
        if (allowable_values.count (value_array[i]) == 0)
        {
            this->show_error_of_using_improper_string_value (
                key,
                i,
                tip_info,
                allowable_values);
        }
    }
}



void
InputDatabase::check_disallowable_string_value (
    const std::string& key,
    const std::string& disallowable_value,
    const std::string& tip_info) const
{
    if (!this->key_exists (key))
    {
        return;
    }

    TBOX_ASSERT (this->is_string (key));
    const std::vector<std::string>& value_array = this->get_string_vector (key);
    for (std::size_t i = 0; i < value_array.size(); ++i)
    {
        if (value_array[i] == disallowable_value)
        {
            this->show_error_of_using_improper_string_value (
                key,
                i,
                tip_info);
        }
    }
}



void
InputDatabase::check_allowable_integer_value (
    const std::string& key,
    const int min,
    const int max) const
{
    if (!this->key_exists (key))
    {
        return;
    }

    TBOX_ASSERT (this->is_integer (key));
    const std::vector<int>& value_array = this->get_integer_vector (key);
    for (std::size_t i = 0; i < value_array.size(); ++i)
    {
        const int value = value_array[i];
        if (value < min || value > max)
        {
            std::ostringstream os;
            os << key;
            if (value_array.size() > 1)
            {
                os << "[" << i << "]";
            }
            os << " = " << value << " is not supported, \n";
            os << "it should take value from " << min << " to " << max << '\n';

            this->show_unknown_error (
                key,
                os.str());
        }
    }
}



void
InputDatabase::check_allowable_min_integer_value (
    const std::string& key,
    const int min) const
{
    if (!this->key_exists (key))
    {
        return;
    }

    TBOX_ASSERT (this->is_integer (key));
    const std::vector<int>& value_array = this->get_integer_vector (key);
    for (std::size_t i = 0; i < value_array.size(); ++i)
    {
        const int value = value_array[i];
        if (value < min)
        {
            std::ostringstream os;
            os << key;
            if (value_array.size() > 1)
            {
                os << "[" << i << "]";
            }
            os << " = " << value << " is not supported, \n";
            os << "it should take value greater than " << min << '\n';

            this->show_unknown_error (
                key,
                os.str());
        }
    }
}



void
InputDatabase::check_allowable_max_integer_value (
    const std::string& key,
    const int max) const
{
    if (!this->key_exists (key))
    {
        return;
    }

    TBOX_ASSERT (this->is_integer (key));
    const std::vector<int>& value_array = this->get_integer_vector (key);
    for (std::size_t i = 0; i < value_array.size(); ++i)
    {
        const int value = value_array[i];
        if (value > max)
        {
            std::ostringstream os;
            os << key;
            if (value_array.size() > 1)
            {
                os << "[" << i << "]";
            }
            os << " = " << value << " is not supported, \n";
            os << "it should take value less than " << max << '\n';

            this->show_unknown_error (
                key,
                os.str());
        }
    }
}



void
InputDatabase::check_allowable_double_value (
    const std::string& key,
    const double min,
    const double max) const
{
    if (!this->key_exists (key))
    {
        return;
    }

    TBOX_ASSERT (this->is_double (key));
    const std::vector<double>& value_array = this->get_double_vector (key);
    for (std::size_t i = 0; i < value_array.size(); ++i)
    {
        const double value = value_array[i];
        if (value < min || value > max)
        {
            std::ostringstream os;
            os << key;
            if (value_array.size() > 1)
            {
                os << "[" << i << "]";
            }
            os << " = " << value << " is not supported, \n";
            os << "it should take value from " << min << " to " << max << '\n';

            this->show_unknown_error (
                key,
                os.str());
        }
    }
}



void
InputDatabase::check_allowable_min_double_value (
    const std::string& key,
    const double min) const
{
    if (!this->key_exists (key))
    {
        return;
    }

    TBOX_ASSERT (this->is_double (key));
    const std::vector<double>& value_array = this->get_double_vector (key);
    for (std::size_t i = 0; i < value_array.size(); ++i)
    {
        const double value = value_array[i];
        if (value < min)
        {
            std::ostringstream os;
            os << key;
            if (value_array.size() > 1)
            {
                os << "[" << i << "]";
            }
            os << " = " << value << " is not supported, \n";
            os << "it should take value greater than " << min << '\n';

            this->show_unknown_error (
                key,
                os.str());
        }
    }
}



void
InputDatabase::check_allowable_max_double_value (
    const std::string& key,
    const double max) const
{
    if (!this->key_exists (key))
    {
        return;
    }

    TBOX_ASSERT (this->is_double (key));
    const std::vector<double>& value_array = this->get_double_vector (key);
    for (std::size_t i = 0; i < value_array.size(); ++i)
    {
        const double value = value_array[i];
        if (value > max)
        {
            std::ostringstream os;
            os << key;
            if (value_array.size() > 1)
            {
                os << "[" << i << "]";
            }
            os << " = " << value << " is not supported, \n";
            os << "it should take value less than " << max << '\n';

            this->show_unknown_error (
                key,
                os.str());
        }
    }
}



void
InputDatabase::check_vec_size_of_dim(
    const std::string& key,
    const int dim) const
{
    if (!this->key_exists(key))
    {
        return;
    }
    
    if (this->get_array_size(key) != dim)
    {
        std::ostringstream os;
        os << key;
        os << "key's size not equal to dim" << dim << "\n";
        
        this->show_error_of_disallowable_array_size(key, dim, os.str ());
    }
}
        
    
    
void 
InputDatabase::check_vec_size(
    const std::string& key,
    const int allowable_size) const
{
    if (!this->key_exists (key))
    {
        return;
    }
    
    if (this->get_array_size (key) != allowable_size)
    {
        
        std::ostringstream os;
        os << key;
        os << "key's size not equal to allowable size" << allowable_size << "\n";
        
        this->show_error_of_disallowable_array_size(key, allowable_size, os.str ());
    }
}



void
InputDatabase::check_same_size (
    const std::string& key_1,
    const std::string& key_2) const
{
    if (!this->key_exists (key_1) || !this->key_exists (key_2))
    {
        return;
    }

    if (this->get_array_size (key_1) != this->get_array_size (key_2))
    {
        this->show_error_of_disallowable_different_size (
            key_1,
            key_2);
    }
}



bool
InputDatabase::is_database (
    const std::string& key) const
{
    TBOX_ASSERT (d_database);
    return d_database->isDatabase (key);
}



InputDatabase*
InputDatabase::put_database (
    const std::string& key) const
{
    TBOX_ASSERT (d_database);

    InputDatabase* sub_db (new InputDatabase (d_database->putDatabase (key)));

    sub_db->d_is_top_db = false;
    if (d_is_top_db)
    {
        sub_db->d_nested_name = key;
    }
    else
    {
        sub_db->d_nested_name = d_nested_name + " --> " + key;
    }

    sub_db->d_parent_db = const_cast<InputDatabase*> (this);
    const_cast<InputDatabase*> (this)->d_sub_databases.insert (std::make_pair (key, sub_db));

    return sub_db;
}



const InputDatabase*
InputDatabase::get_database (
    const std::string& key) const
{
    //std::cout << "when get database, key = " << key << std::endl;
    TBOX_ASSERT (d_sub_databases.count (key) > 0);
    return d_sub_databases.find (key)->second;
}



bool
InputDatabase::is_bool (
    const std::string& key) const
{
    TBOX_ASSERT (d_database);
    return d_database->isBool (key);
}



void
InputDatabase::put_bool (
    const std::string& key,
    const bool& data) const
{
    TBOX_ASSERT (d_database);
    d_database->putBool (key, data);
}



void
InputDatabase::put_bool_vector (
    const std::string& key,
    const std::vector<bool>& data) const
{
    TBOX_ASSERT (d_database);
    d_database->putBoolVector (key, data);
}



void
InputDatabase::put_bool_array (
    const std::string& key,
    const bool* const data,
    const int nelements) const
{
    TBOX_ASSERT (d_database);
    d_database->putBoolArray (key, data, nelements);
}



bool
InputDatabase::get_bool (
    const std::string& key) const
{
    this->check_requisite_key (key, DataType::BOOL, DefaultSwitch::OFF, ArraySize::ONE);
    const std::vector<bool>& value = this->get_bool_vector (key);
    if (value.size() != 1)
    {
        this->show_error_of_using_array (key);
    }

    return value[0];
}



bool
InputDatabase::get_bool_with_default (
    const std::string& key,
    const bool& default_value) const
{
    if (this->key_exists (key))
    {
        if (this->is_string (key))
        {
            const std::vector<std::string>& value = this->get_string_vector (key);
            if (value.size() == 1 && value[0] == "DOGOS_DEFAULT_VALUE")
            {
                return default_value;
            }
            else
            {
                this->show_error_of_disallowable_type (
                    key,
                    "BOOL");
            }
        }
        else
        {
            TBOX_ASSERT (this->is_bool (key));
            return this->get_bool (key);
        }
    }

    return default_value;
}



std::vector<bool>
InputDatabase::get_bool_vector (
    const std::string& key) const
{
    TBOX_ASSERT (d_database && d_database->isBool (key));
    return d_database->getBoolVector (key);
}



void
InputDatabase::get_bool_array (
    const std::string& key,
    bool* data,
    const int nelements) const
{
    TBOX_ASSERT (d_database && d_database->isBool (key));
    d_database->getBoolArray (key, data, nelements);
}



void
InputDatabase::get_bool_vector_with_default (
    const std::string& key,
    const bool default_value,
    const int default_size,
    std::vector<bool>& result_array) const
{
    if (!this->key_exists (key))
    {
        TBOX_ASSERT (default_size >= 0);
        result_array.resize (default_size, default_value);
    }
    else
    {
        if (this->is_string (key))
        {
            const std::vector<std::string>& value = this->get_string_vector (key);
            if (value.size() == 1 && value[0] == "DOGOS_DEFAULT_VALUE")
            {
                TBOX_ASSERT (default_size >= 0);
                result_array.resize (default_size, default_value);
                return;
            }
            else
            {
                this->show_error_of_disallowable_type (
                    key,
                    "BOOL");
            }
        }
        else
        {
            TBOX_ASSERT (this->is_bool (key));
            result_array = this->get_bool_vector (key);
        }
    }
}



bool
InputDatabase::is_char (
    const std::string& key) const
{
    TBOX_ASSERT (d_database);
    return d_database->isChar (key);
}



void
InputDatabase::put_char (
    const std::string& key,
    const char& data) const
{
    TBOX_ASSERT (d_database);
    d_database->putChar (key, data);
}



void
InputDatabase::put_char_vector (
    const std::string& key,
    const std::vector<char>& data) const
{
    TBOX_ASSERT (d_database);
    d_database->putCharVector (key, data);
}



void
InputDatabase::put_char_array (
    const std::string& key,
    const char* const data,
    const int nelements) const
{
    TBOX_ASSERT (d_database);
    d_database->putCharArray (key, data, nelements);
}



char
InputDatabase::get_char (
    const std::string& key) const
{
    this->check_requisite_key (key, DataType::CHAR, DefaultSwitch::OFF, ArraySize::ONE);
    const std::vector<char>& value = this->get_char_vector (key);
    if (value.size() != 1)
    {
        this->show_error_of_using_array (key);
    }

    return value[0];
}



char
InputDatabase::get_char_with_default (
    const std::string& key,
    const char& default_value) const
{
    if (this->key_exists (key))
    {
        if (this->is_string (key))
        {
            const std::vector<std::string>& value = this->get_string_vector (key);
            if (value.size() == 1 && value[0] == "DOGOS_DEFAULT_VALUE")
            {
                return default_value;
            }
            else
            {
                this->show_error_of_disallowable_type (
                    key,
                    "CHAR");
            }
        }
        else
        {
            TBOX_ASSERT (this->is_char (key));
            return this->get_char (key);
        }
    }

    return default_value;
}



std::vector<char>
InputDatabase::get_char_vector (
    const std::string& key) const
{
    TBOX_ASSERT (d_database && d_database->isChar (key));
    return d_database->getCharVector (key);
}



void
InputDatabase::get_char_array (
    const std::string& key,
    char* data,
    const int nelements) const
{
    TBOX_ASSERT (d_database);
    d_database->getCharArray (key, data, nelements);
}



void
InputDatabase::get_char_vector_with_default (
    const std::string& key,
    const char default_value,
    const int default_size,
    std::vector<char>& result_array) const
{
    if (!this->key_exists (key))
    {
        TBOX_ASSERT (default_size >= 0);
        result_array.resize (default_size, default_value);
    }
    else
    {
        if (this->is_string (key))
        {
            const std::vector<std::string>& value = this->get_string_vector (key);
            if (value.size() == 1 && value[0] == "DOGOS_DEFAULT_VALUE")
            {
                TBOX_ASSERT (default_size >= 0);
                result_array.resize (default_size, default_value);
                return;
            }
            else
            {
                this->show_error_of_disallowable_type (
                    key,
                    "CHAR");
            }
        }
        else
        {
            TBOX_ASSERT (this->is_char (key));
            result_array = this->get_char_vector (key);
        }
    }
}



bool
InputDatabase::is_complex (
    const std::string& key) const
{
    TBOX_ASSERT (d_database);
    return d_database->isComplex (key);
}



void
InputDatabase::put_complex (
    const std::string& key,
    const Complex& data) const
{
    TBOX_ASSERT (d_database);
    d_database->putComplex (key, data);
}



void
InputDatabase::put_complex_vector (
    const std::string& key,
    const std::vector<Complex>& data) const
{
    TBOX_ASSERT (d_database);
    d_database->putComplexVector (key, data);
}



void
InputDatabase::put_complex_array (
    const std::string& key,
    const Complex* const data,
    const int nelements) const
{
    TBOX_ASSERT (d_database);
    d_database->putComplexArray (key, data, nelements);
}



Complex
InputDatabase::get_complex (
    const std::string& key) const
{
    TBOX_ASSERT (d_database && d_database->isComplex (key));
    return d_database->getComplex (key);
}



Complex
InputDatabase::get_complex_with_default (
    const std::string& key,
    const Complex& defaultvalue) const
{
    TBOX_ASSERT (d_database);
    return d_database->getComplexWithDefault (key, defaultvalue);
}



std::vector<Complex>
InputDatabase::get_complex_vector (
    const std::string& key) const
{
    TBOX_ASSERT (d_database && d_database->isComplex (key));
    return d_database->getComplexVector (key);
}



void
InputDatabase::get_complex_array (
    const std::string& key,
    Complex* data,
    const int nelements) const
{
    TBOX_ASSERT (d_database && d_database->isComplex (key));
    d_database->getComplexArray (key, data, nelements);
}



bool
InputDatabase::is_double (
    const std::string& key) const
{
    TBOX_ASSERT (d_database);
    return d_database->isDouble (key);
}



void
InputDatabase::put_double (
    const std::string& key,
    const double& data) const
{
    TBOX_ASSERT (d_database);
    d_database->putDouble (key, data);
}



void
InputDatabase::put_double_vector (
    const std::string& key,
    const std::vector<double>& data) const
{
    TBOX_ASSERT (d_database);
    d_database->putDoubleVector (key, data);
}



void
InputDatabase::put_double_array (
    const std::string& key,
    const double* const data,
    const int nelements) const
{
    TBOX_ASSERT (d_database);
    d_database->putDoubleArray (key, data, nelements);
}



double
InputDatabase::get_double (
    const std::string& key) const
{
    this->check_requisite_key (key, DataType::DOUBLE, DefaultSwitch::OFF, ArraySize::ONE);
    const std::vector<double>& value = this->get_double_vector (key);
    if (value.size() != 1)
    {
        this->show_error_of_using_array (key);
    }

    return value[0];
}



double
InputDatabase::get_double_with_default (
    const std::string& key,
    const double& default_value) const
{
    if (this->key_exists (key))
    {
        if (this->is_string (key))
        {
            const std::vector<std::string>& value = this->get_string_vector (key);
            if (value.size() == 1 && value[0] == "DOGOS_DEFAULT_VALUE")
            {
                return default_value;
            }
            else
            {
                this->show_error_of_disallowable_type (
                    key,
                    "DOUBLE");
            }
        }
        else
        {
            TBOX_ASSERT (this->is_double (key));
            return this->get_double (key);
        }
    }

    return default_value;
}



std::vector<double>
InputDatabase::get_double_vector (
    const std::string& key) const
{
    TBOX_ASSERT (d_database && d_database->isDouble (key));
    return d_database->getDoubleVector (key);
}



void
InputDatabase::get_double_array (
    const std::string& key,
    double* data,
    const int nelements) const
{
    TBOX_ASSERT (d_database);
    d_database->getDoubleArray (key, data, nelements);
}



void
InputDatabase::get_double_vector_with_default (
    const std::string& key,
    const double default_value,
    const int default_size,
    std::vector<double>& result_array) const
{
    if (!this->key_exists (key))
    {
        TBOX_ASSERT (default_size >= 0);
        result_array.resize (default_size, default_value);
    }
    else
    {
        if (this->is_string (key))
        {
            const std::vector<std::string>& value = this->get_string_vector (key);
            if (value.size() == 1 && value[0] == "DOGOS_DEFAULT_VALUE")
            {
                TBOX_ASSERT (default_size >= 0);
                result_array.resize (default_size, default_value);
                return;
            }
            else
            {
                this->show_error_of_disallowable_type (
                    key,
                    "DOUBLE");
            }
        }
        else
        {
            TBOX_ASSERT (this->is_double (key));
            result_array = this->get_double_vector (key);
        }
    }
}



bool
InputDatabase::is_float (
    const std::string& key) const
{
    TBOX_ASSERT (d_database);
    return d_database->isFloat (key);
}



void
InputDatabase::put_float (
    const std::string& key,
    const float& data) const
{
    TBOX_ASSERT (d_database);
    d_database->putFloat (key, data);
}



void
InputDatabase::put_float_vector (
    const std::string& key,
    const std::vector<float>& data) const
{
    TBOX_ASSERT (d_database);
    d_database->putFloatVector (key, data);
}



void
InputDatabase::put_float_array (
    const std::string& key,
    const float* const data,
    const int nelements) const
{
    TBOX_ASSERT (d_database);
    d_database->putFloatArray (key, data, nelements);
}



float
InputDatabase::get_float (
    const std::string& key) const
{
    TBOX_ASSERT (d_database && d_database->isFloat (key));
    return d_database->getFloat (key);
}



float
InputDatabase::get_float_with_default (
    const std::string& key,
    const float& defaultvalue) const
{
    TBOX_ASSERT (d_database);
    return d_database->getFloatWithDefault (key, defaultvalue);
}



std::vector<float>
InputDatabase::get_float_vector (
    const std::string& key) const
{
    TBOX_ASSERT (d_database && d_database->isFloat (key));
    return d_database->getFloatVector (key);
}



void
InputDatabase::get_float_array (
    const std::string& key,
    float* data,
    const int nelements) const
{
    TBOX_ASSERT (d_database && d_database->isFloat (key));
    d_database->getFloatArray (key, data, nelements);
}



bool
InputDatabase::is_integer (
    const std::string& key) const
{
    TBOX_ASSERT (d_database);
    return d_database->isInteger (key);
}



void
InputDatabase::put_integer (
    const std::string& key,
    const int& data) const
{
    TBOX_ASSERT (d_database);
    d_database->putInteger (key, data);
}



void
InputDatabase::put_integer_vector (
    const std::string& key,
    const std::vector<int>& data) const
{
    TBOX_ASSERT (d_database);
    d_database->putIntegerVector (key, data);
}



void
InputDatabase::put_integer_array (
    const std::string& key,
    const int* const data,
    const int nelements) const
{
    TBOX_ASSERT (d_database);
    d_database->putIntegerArray (key, data, nelements);
}



int
InputDatabase::get_integer (
    const std::string& key) const
{
    this->check_requisite_key (key, DataType::INTEGER, DefaultSwitch::OFF, ArraySize::ONE);
    const std::vector<int>& value = this->get_integer_vector (key);
    if (value.size() != 1)
    {
        this->show_error_of_using_array (key);
    }

    return value[0];
}



int
InputDatabase::get_integer_with_default (
    const std::string& key,
    const int& default_value) const
{
    if (this->key_exists (key))
    {
        if (this->is_string (key))
        {
            const std::vector<std::string>& value = this->get_string_vector (key);
            if (value.size() == 1 && value[0] == "DOGOS_DEFAULT_VALUE")
            {
                return default_value;
            }
            else
            {
                this->show_error_of_disallowable_type (
                    key,
                    "INTEGER");
            }
        }
        else
        {
            TBOX_ASSERT (this->is_integer (key));
            return this->get_integer (key);
        }
    }

    return default_value;
}



std::vector<int>
InputDatabase::get_integer_vector (
    const std::string& key) const
{
    TBOX_ASSERT (d_database && d_database->isInteger (key));
    return d_database->getIntegerVector (key);
}



void
InputDatabase::get_integer_array (
    const std::string& key,
    int* data,
    const int nelements) const
{
    TBOX_ASSERT (d_database && d_database->isInteger (key));
    d_database->getIntegerArray (key, data, nelements);
}



void
InputDatabase::get_integer_vector_with_default (
    const std::string& key,
    const int default_value,
    const int default_size,
    std::vector<int>& result_array) const
{
    if (!this->key_exists (key))
    {
        TBOX_ASSERT (default_size >= 0);
        result_array.resize (default_size, default_value);
    }
    else
    {
        if (this->is_string (key))
        {
            const std::vector<std::string>& value = this->get_string_vector (key);
            if (value.size() == 1 && value[0] == "DOGOS_DEFAULT_VALUE")
            {
                TBOX_ASSERT (default_size >= 0);
                result_array.resize (default_size, default_value);
                return;
            }
            else
            {
                this->show_error_of_disallowable_type (
                    key,
                    "INTEGER");
            }
        }
        else
        {
            TBOX_ASSERT (this->is_integer (key));
            result_array = this->get_integer_vector (key);
        }
    }
}



bool
InputDatabase::is_string (
    const std::string& key) const
{
    TBOX_ASSERT (d_database);
    return d_database->isString (key);
}



void
InputDatabase::put_string (
    const std::string& key,
    const std::string& data) const
{
    TBOX_ASSERT (d_database);
    d_database->putString (key, data);
}



void
InputDatabase::put_string_vector (
    const std::string& key,
    const std::vector<std::string>& data) const
{
    TBOX_ASSERT (d_database);
    d_database->putStringVector (key, data);
}



void
InputDatabase::put_string_array (
    const std::string& key,
    const std::string* const data,
    const int nelements) const
{
    TBOX_ASSERT (d_database);
    d_database->putStringArray (key, data, nelements);
}



std::string
InputDatabase::get_string (
    const std::string& key) const
{
    this->check_requisite_key (key, DataType::STRING, DefaultSwitch::OFF, ArraySize::ONE);
    const std::vector<std::string>& value = this->get_string_vector (key);

    if (value.size() != 1)
    {
        this->show_error_of_using_array (key);
    }

    return value[0];
}



std::string
InputDatabase::get_string_with_default (
    const std::string& key,
    const std::string& default_value) const
{
    if (this->key_exists (key))
    {
        TBOX_ASSERT (this->is_string (key));

        const std::vector<std::string>& value = this->get_string_vector (key);
        if (value.size() == 1 && value[0] == "DOGOS_DEFAULT_VALUE")
        {
            return default_value;
        }

        return this->get_string (key);
    }

    return default_value;
}



std::vector<std::string>
InputDatabase::get_string_vector (
    const std::string& key) const
{
    TBOX_ASSERT (d_database && d_database->isString (key));
    return d_database->getStringVector (key);
}



void
InputDatabase::get_string_array (
    const std::string& key,
    std::string* data,
    const int nelements) const
{
    TBOX_ASSERT (d_database && d_database->isString (key));
    d_database->getStringArray (key, data, nelements);
}



void
InputDatabase::get_string_vector_with_default (
    const std::string& key,
    const std::string& default_value,
    const int default_size,
    std::vector<std::string>& result_array) const
{
    if (!this->key_exists (key))
    {
        TBOX_ASSERT (default_size >= 0);
        result_array.resize (default_size, default_value);
    }
    else
    {
        TBOX_ASSERT (this->is_string (key));
        result_array = this->get_string_vector (key);
        if (result_array.size() == 1 && result_array[0] == "DOGOS_DEFAULT_VALUE")
        {
            TBOX_ASSERT (default_size >= 0);
            result_array.resize (default_size, default_value);
        }
    }
}



std::ostream&
operator << (
    std::ostream& os,
    const InputDatabase& rhs)
{
    rhs.d_database->printClassData (os);
    return os;
}



std::string
InputDatabase::get_file_name()const
{
    if (d_is_top_db)
    {
        return d_database->getName();
    }
    else
    {
        return d_parent_db->get_file_name();
    }
}



void
InputDatabase::show_error_of_using_default_value (
    const std::string& key) const
{
    std::ostringstream os;

    os << this->get_line_number_message (key) << '\n';
    os << "not supported of \"" << key << "\" = \"DOGOS_DEFAULT_VALUE\".";

    TBOX_ERROR (LogMessage::get_log_info (os, ONE_TAB));
}



void
InputDatabase::show_error_of_missing_key(
    const std::string& key) const
{
    std::ostringstream os;

    os << this->get_line_number_message (key) << '\n';
    os << "keyword \"" << key << "\" MUST be set.";

    TBOX_ERROR (LogMessage::get_log_info (os, ONE_TAB));
}



void
InputDatabase::show_error_of_using_blank_string(
    const std::string& key) const
{
    std::ostringstream os;
    this->check_requisite_key (key, DataType::STRING, DefaultSwitch::OFF, ArraySize::ANY);

    os << this->get_line_number_message (key) << '\n';
    os << "\"" << key << "\" can't have space string.";

    TBOX_ERROR (LogMessage::get_log_info (os, ONE_TAB));
}



void
InputDatabase::show_error_of_using_array(
    const std::string& key) const
{
    TBOX_ASSERT (this->key_exists (key));
    TBOX_ASSERT_GT (this->get_array_size (key), 1);

    std::ostringstream os;

    os << this->get_line_number_message (key) << '\n';
    os << "the size of \"" << key << "\" is " << this->get_array_size (key) << '\n';
    os << "it should be a scalar type(size = 1) instead of an vector type";

    TBOX_ERROR (LogMessage::get_log_info (os, ONE_TAB));
}



void
InputDatabase::show_error_of_disallowable_type(
    const std::string& key,
    const std::string& allowable_type) const
{
    TBOX_ASSERT (this->key_exists (key));

    std::ostringstream os;

    os << this->get_line_number_message (key) << '\n';
    os << "not supported type of \"" << key << "\" whose type is \"";
    os << Utilities::enum_to_string (this->get_key_type (key)) << "\"\n";
    os << "the allowable type is: " << " \"" << allowable_type << "\"";

    TBOX_ERROR (LogMessage::get_log_info (os, ONE_TAB));
}



void
InputDatabase::show_error_of_disallowable_type(
    const std::string& key,
    const std::set<std::string>& allowable_types) const
{
    TBOX_ASSERT (this->key_exists (key));

    std::ostringstream os;

    os << this->get_line_number_message (key) << '\n';
    os << "not supported type of \"" << key << "\" whose type is \"";
    os << Utilities::enum_to_string (this->get_key_type (key)) << "\"\n";
    os << "the allowable types is:\n ";
    os << Utilities::get_rearranged_set (allowable_types, true, 1);

    TBOX_ERROR (LogMessage::get_log_info (os, ONE_TAB));
}



void
InputDatabase::show_error_of_disallowable_mutually_exclusive(
    const std::string& key_1,
    const std::string& key_2) const
{
    TBOX_ASSERT (this->key_exists (key_1) && this->key_exists (key_2));
    std::ostringstream os;

    os << this->get_line_number_message() << '\n';
    os << "\"" << key_1 << "\" and \"" << key_2 << "\" can not set together";

    TBOX_ERROR (LogMessage::get_log_info (os, ONE_TAB));
}



void
InputDatabase::show_error_of_using_improper_string_value(
    const std::string& key,
    const std::string& tip_info,
    const std::set<std::string>& allowable_values) const
{
    this->check_requisite_key (key, DataType::STRING, DefaultSwitch::OFF, ArraySize::ANY);

    const std::vector<std::string>& values = this->get_string_vector (key);
    for (std::size_t pos = 0; pos < values.size(); ++pos)
    {
        const std::string& value = values[pos];
        if (allowable_values.count (value) == 0)
        {
            this->show_error_of_using_improper_string_value (key, pos, tip_info, allowable_values);
        }
    }

    TBOX_FIND_BUG();
}



void
InputDatabase::show_error_of_using_improper_string_value(
    const std::string& key,
    const std::string& value,
    const std::string& error_message) const
{
    this->check_optional_key (key, DataType::STRING, DefaultSwitch::ON, ArraySize::ANY);

    if (this->key_exists (key))
    {
        const std::vector<std::string>& values = this->get_string_vector (key);
        for (std::size_t pos = 0; pos < values.size(); ++pos)
        {
            if (value == values[pos])
            {
                this->show_error_of_using_improper_string_value (key, pos, error_message);
            }
        }

        TBOX_ERROR ("error, not found key = " << key << ", value = " << value);
    }
    else
    {
        std::ostringstream oss;
        oss << "when using default parameter of " << key << " = \"" << value << "\"\n";
        oss << error_message;

        this->show_unknown_error (key, oss.str());
    }
}



void
InputDatabase::show_error_of_using_improper_string_value(
    const std::string& key,
    const std::string& value,
    const std::string& tip_info,
    const std::set<std::string>& allowable_values) const
{
    this->check_optional_key (key, DataType::STRING, DefaultSwitch::ON, ArraySize::ANY);

    if (this->key_exists (key))
    {
        const std::vector<std::string>& values = this->get_string_vector (key);
        for (std::size_t pos = 0; pos < values.size(); ++pos)
        {
            if (value == values[pos])
            {
                this->show_error_of_using_improper_string_value (key, pos, tip_info, allowable_values);
            }
        }

        TBOX_FIND_BUG();
    }
    else
    {
        std::ostringstream oss;
        oss << "when using default parameter of " << key << " = \"" << value << "\"\n";
        oss << tip_info << std::endl;
        if (!allowable_values.empty())
        {
            oss << "the following values are acceptable:\n";

            oss << Utilities::get_rearranged_set (allowable_values, true, 1) << '\n';

            std::string gauss_value;
            bool has_tip = Utilities::get_similar_string(
                               value,
                               allowable_values,
                               gauss_value,
                               3);
            if (has_tip)
            {
                oss << "\ndo you mean \"" << gauss_value << "\" ?" << std::endl;
            }
        }

        this->show_unknown_error (key, oss.str());
    }
}



void
InputDatabase::show_error_of_using_improper_string_value(
    const std::string& key,
    const int num,
    const std::string& tip_info,
    const std::set<std::string>& allowable_values) const
{
    this->check_requisite_key (key, DataType::STRING, DefaultSwitch::OFF, ArraySize::ANY);

    const std::vector<std::string> values = this->get_string_vector (key);
    TBOX_ASSERT (num >= 0 && num < static_cast<int>(values.size()));

    std::ostringstream os;
    os << this->get_line_number_message (key) << '\n';
    os << "unrecognized " << key;
    if (values.size() > 1)
    {
        os << "[" << num << "]";
    }
    os << " = \"" << values[num] << "\"\n";
    os << tip_info << '\n';

    if (!allowable_values.empty())
    {
        os << "the following values are acceptable:\n";

        os << Utilities::get_rearranged_set (allowable_values, true, 1) << '\n';

        std::string gauss_value;
        bool has_tip = Utilities::get_similar_string(
                           values[num],
                           allowable_values,
                           gauss_value,
                           3);
        if (has_tip)
        {
            os << "\ndo you mean \"" << gauss_value << "\" ?" << std::endl;
        }
    }

    TBOX_ERROR (LogMessage::get_log_info (os, ONE_TAB));
}



void
InputDatabase::show_error_of_using_improper_string_value(
    const std::string& key,
    const int num,
    const std::string& error_message) const
{
    this->check_requisite_key (key, DataType::STRING, DefaultSwitch::OFF, ArraySize::ANY);
    const std::vector<std::string> values = this->get_string_vector (key);
    TBOX_ASSERT (num >= 0 && num < static_cast<int>(values.size()));
    std::ostringstream os;
    os << this->get_line_number_message (key) << '\n';
    os << "unrecognized " << key;
    if (values.size() > 1)
    {
        os << "[" << num << "]";
    }
    os << " = \"" << values[num] << "\"\n";
    os << error_message << std::endl;

    TBOX_ERROR (LogMessage::get_log_info (os, ONE_TAB));
}



void
InputDatabase::show_error_of_using_improper_integer_value(
    const std::string& key,
    const int value,
    const std::string& error_message) const
{
    this->check_requisite_key (key, DataType::INTEGER, DefaultSwitch::OFF, ArraySize::ANY);
    const std::vector<int>& values = this->get_integer_vector (key);
    for (std::size_t pos = 0; pos < values.size(); ++pos)
    {
        if (value == values[pos])
        {
            std::ostringstream os;
            os << this->get_line_number_message (key) << '\n';
            os << "error in:\n" << key;
            if (values.size() > 1)
            {
                os << "[" << pos << "]";
            }
            os << " = " << value << '\n';

            os << error_message;

            TBOX_ERROR (LogMessage::get_log_info (os, ONE_TAB));
        }
    }

    TBOX_ERROR ("not found ?");
}



void
InputDatabase::show_error_of_using_improper_integer_value(
    const std::string& key,
    const int num,
    const std::string& tip_info,
    const std::set<int>& allowable_values) const
{
    this->check_requisite_key (key, DataType::INTEGER, DefaultSwitch::OFF, ArraySize::ANY);

    const std::vector<int> values = this->get_integer_vector (key);
    TBOX_ASSERT (num >= 0 && num < static_cast<int>(values.size()));

    std::ostringstream os;
    os << this->get_line_number_message (key) << '\n';
    os << "unrecognized " << key;
    if (values.size() > 1)
    {
        os << "[" << num << "]";
    }
    os << " = \"" << values[num] << "\"\n";
    os << tip_info << '\n';

    if (!allowable_values.empty())
    {
        os << "the following values are acceptable:\n";

        os << Utilities::get_rearranged_set (allowable_values, false, 1) << '\n';
    }

    TBOX_ERROR (LogMessage::get_log_info (os, ONE_TAB));
}



void
InputDatabase::show_error_of_using_improper_double_value(
    const std::string& key,
    const double value,
    const std::string& error_message) const
{
    this->check_requisite_key (key, DataType::DOUBLE, DefaultSwitch::OFF, ArraySize::ANY);
    const std::vector<double>& values = this->get_double_vector (key);
    for (std::size_t pos = 0; pos < values.size(); ++pos)
    {
        if (MathUtilities<double>::equal_eps (value, values[pos]))
        {
            std::ostringstream os;
            os << this->get_line_number_message (key) << '\n';
            os << "error in:\n" << key;
            if (values.size() > 1)
            {
                os << "[" << pos << "]";
            }
            os << " = " << value << '\n';

            os << error_message;

            TBOX_ERROR (LogMessage::get_log_info (os, ONE_TAB));
        }
    }

    TBOX_FIND_BUG();
}



void
InputDatabase::show_error_of_disallowable_different_size (
    const std::string& key_1,
    const std::string& key_2) const
{
    TBOX_ASSERT (this->key_exists (key_1));
    TBOX_ASSERT (this->key_exists (key_2));

    std::ostringstream os;

    os << this->get_line_number_message() << '\n';
    os << "error in\n";
    os << "size of \"" << key_1 << "\" is " << this->get_array_size (key_1) << '\n';
    os << "size of \"" << key_2 << "\" is " << this->get_array_size (key_2) << '\n';
    os << "this is disallowable, they should have same size";

    TBOX_ERROR (LogMessage::get_log_info (os, ONE_TAB));
}



void
InputDatabase::show_error_of_disallowable_array_size(
    const std::string& key,
    const int allowable_size,
    const std::string& error_message) const
{
    std::ostringstream os;

    TBOX_ASSERT (this->key_exists (key));
    os << this->get_line_number_message (key) << '\n';
    os << "\"" << key << "\" have size of " << this->get_array_size (key) << " is not allowable" << '\n';
    os << "it should have a size of " << allowable_size << '\n';
    os << error_message;

    TBOX_ERROR (LogMessage::get_log_info (os, ONE_TAB));
}



void
InputDatabase::show_error_of_unrecognized_key(
    const std::string& unrecognized_key,
    const std::set<std::string>& allowable_keys) const
{
    TBOX_ASSERT (this->key_exists (unrecognized_key));

    std::ostringstream os;

    os << this->get_line_number_message (unrecognized_key) << '\n';
    os << "unrecognized key of \"" << unrecognized_key << "\"\n";
    os << "in the database, the following keys are supported:\n";
    os << Utilities::get_rearranged_set (allowable_keys, true, 1);

    TBOX_ERROR (LogMessage::get_log_info (os, ONE_TAB));
}



void
InputDatabase::show_error_of_unrecognized_key(
    const std::string& unrecognized_key,
    const std::string& suggested_key,
    const std::set<std::string>& allowable_keys) const
{
    TBOX_ASSERT (this->key_exists (unrecognized_key));

    std::ostringstream os;

    os << this->get_line_number_message (unrecognized_key) << '\n';
    os << "unrecognized key of \"" << unrecognized_key << "\"\n";
    os << "in the database, the following keys are supported:\n";
    os << Utilities::get_rearranged_set (allowable_keys, true, 1) << "\n\n";
    os << "do you mean \"" << suggested_key << "\" ?";

    TBOX_ERROR (LogMessage::get_log_info (os, ONE_TAB));
}



void
InputDatabase::show_unknown_error(
    const std::string& error_message) const
{
    std::ostringstream os;
    os << this->get_line_number_message() << '\n';
    os << error_message;

    TBOX_ERROR (LogMessage::get_log_info (os, ONE_TAB));
}



void
InputDatabase::show_unknown_error(
    const std::string& key,
    const std::string& error_message) const
{
    std::ostringstream os;
    os << this->get_line_number_message (key) << '\n';
    os << "error of key = \"" << key << "\"\n";
    os << error_message;

    TBOX_ERROR (LogMessage::get_log_info (os, ONE_TAB));
}



void
InputDatabase::show_error_of_unfound_file(
    const std::string& key,
    const std::string& file_name) const
{
    this->check_optional_key (key, DataType::STRING, DefaultSwitch::OFF, ArraySize::ONE);

    std::ostringstream os;

    if (this->key_exists (key))
    {
        os << this->get_line_number_message (key) << '\n';
        os << "error in:\n" << key << " = " << "\"" << this->get_string (key) << "\"\n";
        os << "can not find the file of \"" << file_name << "\"";
    }
    else
    {
        os << this->get_line_number_message() << '\n';
        os << "error in set key of \"" << key << "\" use default value.\n\n";
        os << "can not find the file of \"" << file_name << "\"";
    }

    TBOX_ERROR (LogMessage::get_log_info (os, ONE_TAB));
}



void
InputDatabase::show_error_of_unfound_directory(
    const std::string& key,
    const std::string& dir_name) const
{
    this->check_optional_key (key, DataType::STRING, DefaultSwitch::OFF, ArraySize::ONE);

    std::ostringstream os;

    if (this->key_exists (key))
    {
        os << this->get_line_number_message (key) << '\n';
        os << "error in:\n" << key << " = " << "\"" << this->get_string (key) << "\"";
        os << "can not find the directory of \"" << dir_name << "\"";
    }
    else
    {
        os << this->get_line_number_message() << '\n';
        os << "error in set key of \"" << key << "\" use default value.\n";
        os << "can not find the directory of \"" << dir_name << "\"";
    }

    TBOX_ERROR (LogMessage::get_log_info (os, ONE_TAB));
}



void
InputDatabase::show_error_of_unsupported_library(
    const std::string& key,
    const std::string& value,
    const std::string& required_library) const
{
    this->show_error_of_using_improper_string_value (
        key,
        value,
        "The current version has not built with supports of library \"" + required_library + "\"");
}



void
InputDatabase::show_error_of_unsupported_library(
    const std::string& key,
    const std::string& required_library) const
{
    this->show_unknown_error(
        key,
        "The current version has not built with supports of library \"" + required_library + "\"");
}

}
